import os
import sys
from typing import List, Dict, Any, Optional, Callable
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
import multiprocessing

# 配置日志

# 确定基础目录
if hasattr(sys, 'frozen'):
    # 运行在打包后的环境中
    base_dir = os.path.dirname(sys.executable)
else:
    # 运行在Python环境中
    base_dir = os.path.dirname(os.path.abspath(__file__))
    base_dir = os.path.abspath(os.path.join(base_dir, '..', '..'))

# 创建日志目录（如果不存在）
log_dir = os.path.join(base_dir, 'logs')
try:
    os.makedirs(log_dir, exist_ok=True)
    print(f"日志目录创建成功: {log_dir}")
except Exception as e:
    print(f"日志目录创建失败: {e}")

# 使用绝对路径设置日志文件
log_file = os.path.join(log_dir, 'duplicate_detector.log')
print(f"日志文件路径: {log_file}")

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler(log_file, encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger('duplicate_detector')


class DuplicateDetector:
    def __init__(self, scan_depth='recursive', hash_algorithm='md5', sort_by='name', sort_order='ascending', show_empty_files=True, use_parallel=True, max_workers=None):
        """初始化重复文件检测器
        
        Args:
            scan_depth: 扫描深度，'recursive'（递归）或'shallow'（浅层）
            hash_algorithm: 哈希算法，如'md5', 'sha1', 'sha256'
            sort_by: 排序依据，'name'（名称）或'size'（大小）
            sort_order: 排序顺序，'ascending'（升序）或'descending'（降序）
            show_empty_files: 是否显示空文件
            use_parallel: 是否使用并行处理
            max_workers: 最大工作线程数，默认为CPU核心数
        """
        self.file_info_list = []
        self.duplicate_groups = []
        self._is_processing = False
        # 存储配置参数
        self.scan_depth = scan_depth
        self.hash_algorithm = hash_algorithm
        self.sort_by = sort_by
        self.sort_order = sort_order
        self.show_empty_files = show_empty_files
        self.use_parallel = use_parallel
        self.max_workers = max_workers or multiprocessing.cpu_count()
        
        # 导入并行组件
        from scanner.parallel_scanner import ParallelScanner
        from hash_calculator.parallel_hash_calculator import ParallelHashCalculator
        from .pipeline_manager import PipelineManager
        
        # 初始化并行组件
        self.parallel_scanner = ParallelScanner(max_workers=self.max_workers)
        self.parallel_hash_calculator = ParallelHashCalculator(algorithm=hash_algorithm, max_workers=self.max_workers)
        self.pipeline_manager = PipelineManager(
            scan_depth=scan_depth,
            hash_algorithm=hash_algorithm,
            max_workers=self.max_workers
        )

    def add_file(self, file_info: Dict[str, Any]) -> bool:
        """
        添加文件信息
        :param file_info: 文件信息字典，必须包含'path', 'size'键
        :return: 是否添加成功
        """
        if not isinstance(file_info, dict) or 'path' not in file_info or 'size' not in file_info:
            logger.error('无效的文件信息')
            return False

        self.file_info_list.append(file_info)
        logger.debug(f'添加文件: {file_info["path"]}')
        return True

    def add_files(self, file_infos: List[Dict[str, Any]]) -> int:
        """
        批量添加文件信息
        :param file_infos: 文件信息字典列表
        :return: 成功添加的文件数量
        """
        count = 0
        for file_info in file_infos:
            if self.add_file(file_info):
                count += 1
        logger.info(f'批量添加了 {count} 个文件')
        return count

    def scan_directory(self, directory: str, progress_callback: Optional[Callable[[int, int], None]] = None, stop_event: Optional[Callable[[], bool]] = None) -> List[List[Dict[str, Any]]]:
        """
        扫描目录并查找重复文件
        :param directory: 要扫描的目录路径
        :param progress_callback: 进度回调函数，接收已处理文件数和总文件数
        :param stop_event: 停止事件函数，返回True表示需要停止处理
        :return: 重复文件组列表
        """
        # 清除之前的结果
        self.clear()
        
        # 检查目录是否存在
        if not os.path.exists(directory):
            logger.error(f'目录不存在: {directory}')
            raise FileNotFoundError(f'目录不存在: {directory}')
        
        if not os.path.isdir(directory):
            logger.error(f'路径不是目录: {directory}')
            raise NotADirectoryError(f'路径不是目录: {directory}')
        
        # 根据是否启用并行处理选择不同的处理方式
        if self.use_parallel:
            logger.info(f'开始并行扫描目录: {directory}')
            
            # 使用流水线管理器进行并行扫描和哈希计算
            # 创建进度回调适配器
            def adapted_progress_callback(stage: str, progress: int, total: int):
                # 只传递progress和total给原始回调函数
                if progress_callback:
                    progress_callback(progress, total)
            
            file_info_list = self.pipeline_manager.scan_and_calculate_hash(
                directory,
                progress_callback=adapted_progress_callback if progress_callback else None,
                stop_event=stop_event
            )
            
            # 检查是否需要停止
            if stop_event and stop_event():
                logger.info('扫描操作已停止')
                return []
        else:
            # 使用传统的顺序扫描方式
            logger.info(f'开始顺序扫描目录: {directory}')
            
            # 在方法内部导入以避免循环导入问题
            from scanner.scanner import Scanner
            from hash_calculator.hash_calculator import HashCalculator
            
            # 创建扫描器
            scanner = Scanner()
            
            # 确定扫描深度
            recursive = self.scan_depth == 'recursive'
            
            # 扫描目录获取文件信息
            file_info_list = scanner.start_scan(
                [directory], 
                recursive=recursive,
                scan_dirs_as_files=False
            )
            
            if not file_info_list:
                logger.warning('未找到文件')
                return []
            
            # 创建哈希计算器并计算文件哈希值
            hash_calculator = HashCalculator(algorithm=self.hash_algorithm)
            logger.info(f'正在计算哈希值 ({len(file_info_list)} 个文件)...')
            
            total_files = len(file_info_list)
            
            for i, file_info in enumerate(file_info_list):
                # 检查是否需要停止
                if stop_event and stop_event():
                    logger.info('扫描操作已停止')
                    return []
                
                # 跳过空文件（如果配置了不显示空文件）
                if not self.show_empty_files and file_info['size'] == 0:
                    continue
                
                # 计算哈希值（对非空文件）
                if file_info['size'] > 0:
                    try:
                        file_hash = hash_calculator.compute_file_hash(file_info['path'])
                        file_info['hash'] = file_hash
                    except Exception as e:
                        logger.error(f'计算文件哈希值时出错: {file_info["path"]}, 错误: {str(e)}')
                        continue
                
                # 更新进度
                if progress_callback:
                    progress_callback(i + 1, total_files)
        
        # 过滤空文件（如果需要）
        if not self.show_empty_files:
            file_info_list = [f for f in file_info_list if f['size'] > 0]
        
        # 添加文件信息到检测器
        self.add_files(file_info_list)
        
        # 查找重复文件
        if self.use_parallel:
            # 并行模式下，直接返回重复文件组
            return self.find_duplicates(progress_callback=progress_callback, stop_event=stop_event)
        else:
            # 顺序模式下，使用现有的find_duplicates方法
            return self.find_duplicates()
        
    def find_duplicates(self, progress_callback: Optional[Callable[[int, int], None]] = None, compare_filename: bool = False, compare_size: bool = True, directory_mode: bool = False, stop_event: Optional[Callable[[], bool]] = None, use_similarity: bool = False, similarity_threshold: float = 95.0) -> List[List[Dict[str, Any]]]:
        """
        查找重复文件或目录
        :param progress_callback: 进度回调函数，接收已处理文件数和总文件数
        :param compare_filename: 是否仅比较文件名
        :param compare_size: 是否比较文件大小
        :param directory_mode: 是否为目录模式（仅比较目录名和大小）
        :param stop_event: 停止事件函数，返回True表示需要停止处理
        :param use_similarity: 是否使用目录名称相似度比对（仅在目录模式下有效）
        :param similarity_threshold: 相似度阈值（0-100），默认95%
        :return: 重复文件组列表
        """
        if self._is_processing:
            logger.warning('重复检测已在进行中')
            return []

        if not self.file_info_list:
            logger.warning('没有文件可检测')
            return []

        self._is_processing = True
        self.duplicate_groups = []

        try:
            total_files = len(self.file_info_list)
            processed_files = 0

            # 检查是否需要停止
            if stop_event and stop_event():
                logger.info('重复检测操作已停止')
                return []

            # 目录模式：直接进行目录重复检测
            if directory_mode:
                logger.info('使用目录模式进行重复检测')
                return self._find_directory_duplicates(progress_callback, compare_filename, compare_size, use_similarity, similarity_threshold)

            # 文件模式：原有逻辑

            # 确定比对方式
            if compare_filename and compare_size:
                logger.info('使用文件名和文件大小进行比对')
            elif compare_filename:
                logger.info('仅使用文件名进行比对')
            elif compare_size:
                logger.info('仅使用文件大小和哈希值进行比对')
            else:
                logger.info('使用默认哈希值进行比对')

            # 第一步：按比对方式分组
            if compare_filename and compare_size:
                # 按文件名和大小分组
                groups = {}
                for file_info in self.file_info_list:
                    # 检查是否需要停止
                    if stop_event and stop_event():
                        logger.info('重复检测操作已停止')
                        return []
                    
                    filename = os.path.basename(file_info['path'])
                    size = file_info['size']
                    key = (filename, size)
                    if key not in groups:
                        groups[key] = []
                    groups[key].append(file_info)

                    processed_files += 1
                    if progress_callback:
                        progress_callback(processed_files, total_files)
            elif compare_filename:
                # 仅按文件名分组
                groups = {}
                for file_info in self.file_info_list:
                    # 检查是否需要停止
                    if stop_event and stop_event():
                        logger.info('重复检测操作已停止')
                        return []
                    
                    filename = os.path.basename(file_info['path'])
                    if filename not in groups:
                        groups[filename] = []
                    groups[filename].append(file_info)

                    processed_files += 1
                    if progress_callback:
                        progress_callback(processed_files, total_files)
            else:
                # 按文件大小分组（默认行为）
                groups = {}
                for file_info in self.file_info_list:
                    # 检查是否需要停止
                    if stop_event and stop_event():
                        logger.info('重复检测操作已停止')
                        return []
                    
                    size = file_info['size']
                    if size == 0:
                        # 空文件单独处理
                        logger.debug(f'忽略空文件: {file_info["path"]}')
                        continue

                    if size not in groups:
                        groups[size] = []
                    groups[size].append(file_info)

                    processed_files += 1
                    if progress_callback:
                        progress_callback(processed_files, total_files)

            # 第二步：根据比对方式进行进一步分组
            if compare_filename and not compare_size:
                # 仅按文件名分组，不需要进一步分组
                for key, files in groups.items():
                    if len(files) >= 2:
                        self.duplicate_groups.append(files)
                        logger.info(f'找到重复文件组: {len(files)} 个文件，文件名: {key}')
            elif compare_filename and compare_size:
                # 按文件名和大小分组，不需要进一步分组
                for (filename, size), files in groups.items():
                    if len(files) >= 2:
                        self.duplicate_groups.append(files)
                        logger.info(f'找到重复文件组: {len(files)} 个文件，文件名: {filename}，大小: {size} 字节')
            else:
                # 按哈希值分组（默认行为）
                for key, files in groups.items():
                    # 检查是否需要停止
                    if stop_event and stop_event():
                        logger.info('重复检测操作已停止')
                        return []
                        
                    if len(files) < 2:
                        continue

                    # 按哈希值分组
                    hash_groups = {}
                    
                    # 如果启用并行处理且文件数量较多，使用多线程计算哈希
                    if self.use_parallel and len(files) > 5:
                        # 创建线程池
                        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                            # 提交所有哈希计算任务
                            future_to_file = {
                                executor.submit(self._calculate_file_hash, file_info['path']): file_info
                                for file_info in files
                                if 'hash' not in file_info or not file_info['hash']
                            }
                            
                            # 处理完成的任务
                            for future in as_completed(future_to_file):
                                file_info = future_to_file[future]
                                try:
                                    file_hash = future.result()
                                    file_info['hash'] = file_hash
                                except Exception as e:
                                    logger.error(f'计算文件哈希时出错: {file_info["path"]}, 错误: {str(e)}')
                                    continue
                                
                                processed_files += 1
                                if progress_callback:
                                    progress_callback(processed_files, total_files)
                                
                                # 检查是否需要停止
                                if stop_event and stop_event():
                                    logger.info('重复检测操作已停止')
                                    executor.shutdown(wait=False, cancel_futures=True)
                                    return []
                    else:
                        # 串行处理
                        for file_info in files:
                            # 检查是否需要停止
                            if stop_event and stop_event():
                                logger.info('重复检测操作已停止')
                                return []
                                
                            if 'hash' not in file_info or not file_info['hash']:
                                try:
                                    file_hash = self._calculate_file_hash(file_info['path'])
                                    file_info['hash'] = file_hash
                                    processed_files += 1
                                    if progress_callback:
                                        progress_callback(processed_files, total_files)
                                except Exception as e:
                                    logger.error(f'计算文件哈希时出错: {file_info["path"]}, 错误: {str(e)}')
                                    continue

                            # 添加到哈希分组中
                            if file_info['hash']:
                                hash_value = file_info['hash']
                                if hash_value not in hash_groups:
                                    hash_groups[hash_value] = []
                                hash_groups[hash_value].append(file_info)

                    # 收集重复文件组
                    for hash_value, duplicate_files in hash_groups.items():
                        if len(duplicate_files) >= 2:
                            self.duplicate_groups.append(duplicate_files)
                            if compare_size:
                                logger.info(f'找到重复文件组: {len(duplicate_files)} 个文件，大小: {key} 字节，哈希值: {hash_value}')
                            else:
                                logger.info(f'找到重复文件组: {len(duplicate_files)} 个文件，哈希值: {hash_value}')


            logger.info(f'重复检测完成，共找到 {len(self.duplicate_groups)} 组重复文件')
            return self.duplicate_groups

        finally:
            self._is_processing = False

    def get_duplicate_groups(self) -> List[List[Dict[str, Any]]]:
        """获取重复文件组列表"""
        return self.duplicate_groups

    def sort_duplicates(self, group_index: int, sort_by: str = 'modified_time', reverse: bool = True) -> bool:
        """
        对指定的重复文件组进行排序
        :param group_index: 重复文件组索引
        :param sort_by: 排序依据，可选' modified_time', 'created_time', 'accessed_time', 'size', 'name'
        :param reverse: 是否降序排列
        :return: 是否排序成功
        """
        if group_index < 0 or group_index >= len(self.duplicate_groups):
            logger.error(f'无效的重复文件组索引: {group_index}')
            return False

        valid_sort_keys = ['modified_time', 'created_time', 'accessed_time', 'size', 'name']
        if sort_by not in valid_sort_keys:
            logger.error(f'无效的排序依据: {sort_by}，有效选项: {valid_sort_keys}')
            return False

        try:
            self.duplicate_groups[group_index].sort(key=lambda x: x.get(sort_by, ''), reverse=reverse)
            logger.info(f'对重复文件组 {group_index} 按 {sort_by} 排序{"降序" if reverse else "升序"}')
            return True
        except Exception as e:
            logger.error(f'排序重复文件组时出错: {str(e)}')
            return False

    def clear(self) -> None:
        """清除所有文件信息和检测结果"""
        self.file_info_list = []
        self.duplicate_groups = []
        logger.info('清除所有文件信息和检测结果')

    def delete_duplicates(self, duplicate_groups=None, group_index: Optional[int] = None, keep_index: int = 0, stop_event: Optional[Callable[[], bool]] = None) -> int:
        """
        删除重复文件
        
        支持两种调用方式：
        1. delete_duplicates(duplicate_groups) - 删除每组中的除了第一个文件以外的所有文件
        2. delete_duplicates(group_index=group_index, keep_index=keep_index) - 删除指定组中的文件，保留指定索引的文件
        
        :param duplicate_groups: 重复文件组列表（如果提供了这个参数，则忽略group_index和keep_index）
        :param group_index: 重复文件组索引
        :param keep_index: 要保留的文件索引，默认为0（第一个文件）
        :param stop_event: 停止事件函数，返回True表示需要停止处理
        :return: 成功删除的文件数量
        """
        deleted_count = 0
        
        # 第一种调用方式：删除每组中的除了第一个文件以外的所有文件
        if duplicate_groups is not None:
            logger.info(f'开始删除重复文件，共 {len(duplicate_groups)} 组')
            for group in duplicate_groups:
                # 检查是否需要停止
                if stop_event and stop_event():
                    logger.info('文件删除操作已停止')
                    break
                    
                if len(group) < 2:
                    continue
                
                # 保留第一个文件，删除其他文件
                for i, file_info in enumerate(group):
                    if i != 0:  # 保留第一个文件
                        file_path = file_info['path']  # 先获取文件路径
                        try:
                            if os.path.exists(file_path):
                                os.remove(file_path)
                                deleted_count += 1
                                logger.info(f'成功删除文件: {file_path}')
                            else:
                                logger.warning(f'文件不存在: {file_path}')
                        except Exception as e:
                            logger.error(f'删除文件时出错: {file_path}, 错误: {str(e)}')
            
            logger.info(f'删除完成，共删除 {deleted_count} 个重复文件')
            return deleted_count
        
        # 第二种调用方式：删除指定组中的文件，保留指定索引的文件
        if group_index is None:
            logger.error('必须提供duplicate_groups或group_index参数')
            return 0
            
        if group_index < 0 or group_index >= len(self.duplicate_groups):
            logger.error(f'无效的重复文件组索引: {group_index}')
            return 0

        group = self.duplicate_groups[group_index]
        if keep_index < 0 or keep_index >= len(group):
            logger.error(f'无效的保留文件索引: {keep_index}')
            return 0

        for i, file_info in enumerate(group):
            # 检查是否需要停止
            if stop_event and stop_event():
                logger.info('文件删除操作已停止')
                break
            if i != keep_index:
                file_path = file_info['path']  # 先获取文件路径
                try:
                    if os.path.exists(file_path):
                        os.remove(file_path)
                        deleted_count += 1
                        logger.info(f'成功删除文件: {file_path}')
                    else:
                        logger.warning(f'文件不存在: {file_path}')
                except Exception as e:
                    logger.error(f'删除文件时出错: {file_path}, 错误: {str(e)}')

        # 更新重复文件组，只保留指定的文件
        self.duplicate_groups[group_index] = [group[keep_index]]
        logger.info(f'在重复文件组 {group_index} 中删除了 {deleted_count} 个文件，保留了 1 个文件')
        return deleted_count

    def _calculate_file_hash(self, file_path: str) -> Optional[str]:
        """
        计算文件哈希值
        :param file_path: 文件路径
        :return: 哈希值或None（如果计算失败）
        """
        try:
            from hash_calculator.hash_calculator import HashCalculator
            hash_calculator = HashCalculator(algorithm=self.hash_algorithm)
            return hash_calculator.compute_file_hash(file_path)
        except Exception as e:
            logger.error(f'计算文件哈希值时出错: {file_path}, 错误: {str(e)}')
            return None

    def _find_directory_duplicates(self, progress_callback: Optional[Callable[[int, int], None]] = None, compare_filename: bool = False, compare_size: bool = True, use_similarity: bool = False, similarity_threshold: float = 95.0) -> List[List[Dict[str, Any]]]:
        """
        查找重复目录，支持多种对比策略
        :param progress_callback: 进度回调函数
        :param compare_filename: 是否比较目录名称
        :param compare_size: 是否比较目录大小
        :param use_similarity: 是否使用目录名称相似度比对
        :param similarity_threshold: 相似度阈值（0-100）
        :return: 重复目录组列表
        """
        duplicate_groups = []
        total_dirs = len(self.file_info_list)
        processed_dirs = 0

        # 确定对比策略
        if use_similarity:
            # 相似度比对模式
            logger.info(f'目录模式：使用相似度比对（阈值: {similarity_threshold}%）')
            # 设置开始时间用于性能监控
            import time
            self._start_time = time.time()
            return self._find_similar_directories(progress_callback, compare_size, similarity_threshold)
        elif compare_filename and compare_size:
            strategy = "name_and_size"
            logger.info('目录模式：按目录名称和大小进行比对')
        elif compare_filename:
            strategy = "name_only"
            logger.info('目录模式：仅按目录名称进行比对')
        elif compare_size:
            strategy = "size_only"
            logger.info('目录模式：仅按目录大小进行比对')
        else:
            # 默认行为：同时比较名称和大小
            strategy = "name_and_size"
            logger.info('目录模式：使用默认策略（目录名称+大小）进行比对')

        # 按选定的策略进行分组
        dir_groups = {}
        for dir_info in self.file_info_list:
            # 检查是否为目录类型
            if dir_info.get('type') != 'directory':
                logger.debug(f'跳过非目录项: {dir_info.get("path", "unknown")}, 类型: {dir_info.get("type", "unknown")}')
                continue
                
            dir_name = dir_info.get('name', os.path.basename(dir_info['path']))
            dir_size = dir_info['size']
            
            # 根据对比策略生成分组键
            if strategy == "name_only":
                key = dir_name
            elif strategy == "size_only":
                key = dir_size
            else:  # name_and_size
                key = (dir_name, dir_size)
            
            logger.debug(f'处理目录: {dir_info["path"]}, 名称: {dir_name}, 大小: {dir_size}, 策略: {strategy}, 键: {key}')
            
            if key not in dir_groups:
                dir_groups[key] = []
            dir_groups[key].append(dir_info)
            
            processed_dirs += 1
            if progress_callback:
                progress_callback(processed_dirs, total_dirs)

        # 找出重复组（包含2个或更多目录）
        for key, dirs in dir_groups.items():
            if len(dirs) >= 2:
                duplicate_groups.append(dirs)
                # 根据策略输出不同的日志信息
                if strategy == "name_only":
                    logger.info(f'找到重复目录组: {len(dirs)} 个目录，名称: {key}')
                elif strategy == "size_only":
                    logger.info(f'找到重复目录组: {len(dirs)} 个目录，大小: {key} 字节')
                else:  # name_and_size
                    dir_name, dir_size = key
                    logger.info(f'找到重复目录组: {len(dirs)} 个目录，名称: {dir_name}，大小: {dir_size} 字节')

        logger.info(f'目录重复检测完成，共找到 {len(duplicate_groups)} 组重复目录')
        return duplicate_groups
        
    def _find_similar_directories(self, progress_callback: Optional[Callable[[int, int], None]] = None, compare_size: bool = True, similarity_threshold: float = 95.0) -> List[List[Dict[str, Any]]]:
        """
        使用多线程相似度算法查找相似目录
        :param progress_callback: 进度回调函数
        :param compare_size: 是否同时比較目录大小
        :param similarity_threshold: 相似度阈值（0-100）
        :return: 相似目录组列表
        """
        # 导入多线程相似度模块
        try:
            from multi_thread_similarity import MultiThreadSimilarityMatcher
        except ImportError:
            logger.error('无法导入多线程相似度模块，使用单线程处理')
            return self._find_similar_directories_fallback(progress_callback, compare_size, similarity_threshold)
        
        directories = [d for d in self.file_info_list if d.get('type') == 'directory']
        total_dirs = len(directories)
        total_comparisons = total_dirs * (total_dirs - 1) // 2
        
        logger.info(f'开始多线程相似度比对，总计 {total_dirs} 个目录，{total_comparisons} 次比较')
        
        # 创建多线程匹配器
        matcher = MultiThreadSimilarityMatcher()
        
        # 执行相似度比对
        duplicate_groups = matcher.find_similar_directories(
            directories, progress_callback, compare_size, similarity_threshold
        )
        
        logger.info(f'多线程相似度检测完成，共找到 {len(duplicate_groups)} 组相似目录')
        return duplicate_groups
    
    def _find_similar_directories_fallback(self, progress_callback: Optional[Callable[[int, int], None]] = None, compare_size: bool = True, similarity_threshold: float = 95.0) -> List[List[Dict[str, Any]]]:
        """
        单线程备用相似度比对（当多线程模块不可用时）
        """
        # 导入相似度计算模块
        try:
            from string_similarity import calculate_directory_name_similarity, is_similar_by_threshold
        except ImportError:
            logger.error('无法导入相似度计算模块，请检查 string_similarity.py 文件')
            return []
        
        duplicate_groups = []
        directories = [d for d in self.file_info_list if d.get('type') == 'directory']
        total_dirs = len(directories)
        
        # 性能保护：单线程模式下限制处理数量
        MAX_DIRS_FOR_SINGLE_THREAD = 1000
        if total_dirs > MAX_DIRS_FOR_SINGLE_THREAD:
            logger.warning(f'单线程模式下目录数量过多（{total_dirs}），将只处理前{MAX_DIRS_FOR_SINGLE_THREAD}个目录')
            directories = directories[:MAX_DIRS_FOR_SINGLE_THREAD]
            total_dirs = len(directories)
        
        total_comparisons = total_dirs * (total_dirs - 1) // 2
        processed_comparisons = 0
        
        logger.info(f'使用单线程备用模式，总计 {total_dirs} 个目录，{total_comparisons} 次比较')
        
        # 已处理的目录索引，避免重复分组
        processed_indices = set()
        
        for i in range(total_dirs):
            if i in processed_indices:
                continue
                
            dir1 = directories[i]
            similar_group = [dir1]
            
            for j in range(i + 1, total_dirs):
                if j in processed_indices:
                    continue
                    
                dir2 = directories[j]
                processed_comparisons += 1
                
                # 更新进度
                if progress_callback:
                    progress_callback(processed_comparisons, total_comparisons)
                
                # 性能保护：超时30秒停止
                if processed_comparisons % 1000 == 0:
                    import time
                    if hasattr(self, '_start_time') and time.time() - self._start_time > 30:
                        logger.warning('单线程相似度比对超时30秒，已停止')
                        break
                
                # 先检查大小是否相同（如果启用了大小比较）
                if compare_size and dir1['size'] != dir2['size']:
                    continue
                
                # 检查目录名称相似度
                dir1_name = dir1.get('name', os.path.basename(dir1['path']))
                dir2_name = dir2.get('name', os.path.basename(dir2['path']))
                
                if is_similar_by_threshold(dir1_name, dir2_name, similarity_threshold):
                    similar_group.append(dir2)
                    processed_indices.add(j)
                    
                    similarity = calculate_directory_name_similarity(dir1_name, dir2_name)
                    logger.debug(f'找到相似目录: "{dir1_name}" vs "{dir2_name}", 相似度: {similarity:.2f}%')
            
            # 如果找到相似目录，添加到结果中
            if len(similar_group) >= 2:
                duplicate_groups.append(similar_group)
                processed_indices.add(i)
                
                group_names = [d.get('name', os.path.basename(d['path'])) for d in similar_group]
                logger.info(f'找到相似目录组（{len(similar_group)} 个）: {", ".join(group_names)}')
        
        logger.info(f'单线程备用相似度检测完成，共找到 {len(duplicate_groups)} 组相似目录，处理了 {processed_comparisons} 次比较')
        return duplicate_groups


if __name__ == '__main__':
    # 测试重复文件检测器
    def progress_callback(processed: int, total: int):
        print(f'处理文件: {processed}/{total}')

    # 创建测试文件信息
    test_files = [
        {'path': 'file1.txt', 'size': 100, 'modified_time': 1630000000, 'hash': 'abc123'},
        {'path': 'file2.txt', 'size': 100, 'modified_time': 1630000100, 'hash': 'abc123'},
        {'path': 'file3.txt', 'size': 200, 'modified_time': 1630000200, 'hash': 'def456'},
        {'path': 'file4.txt', 'size': 200, 'modified_time': 1630000300, 'hash': 'def456'},
        {'path': 'file5.txt', 'size': 150, 'modified_time': 1630000400, 'hash': 'ghi789'},
        {'path': 'file6.txt', 'size': 100, 'modified_time': 1630000500, 'hash': 'jkl012'},
    ]

    # 测试检测器
    detector = DuplicateDetector()
    detector.add_files(test_files)
    duplicates = detector.find_duplicates(progress_callback=progress_callback)

    print(f'找到 {len(duplicates)} 组重复文件:')
    for i, group in enumerate(duplicates):
        print(f'组 {i+1}:')
        for file_info in group:
            print(f'  {file_info["path"]} - 大小: {file_info["size"]} 字节, 修改时间: {file_info["modified_time"]}')

    # 测试排序
    detector.sort_duplicates(0, sort_by='modified_time', reverse=False)
    print('\n排序后的第一组:')
    for file_info in detector.get_duplicate_groups()[0]:
        print(f'  {file_info["path"]} - 修改时间: {file_info["modified_time"]}')